package com.github.aloxc.plugin.restplus.navigator;


import com.github.aloxc.plugin.restplus.common.Consts;
import com.github.aloxc.plugin.restplus.component.ServiceModuleNode;
import com.github.aloxc.plugin.restplus.component.ServiceNode;
import com.github.aloxc.plugin.restplus.component.ServiceRootNode;
import com.github.aloxc.plugin.restplus.component.ServiceTreeNodeCellRenderer;
import com.github.aloxc.plugin.restplus.entity.Group;
import com.github.aloxc.plugin.restplus.entity.RestConfig;
import com.github.aloxc.plugin.restplus.entity.Sort;
import com.github.aloxc.plugin.restplus.entity.SwaggerConfig;
import com.github.aloxc.plugin.restplus.listener.UrlTreeSelectionListener;
import com.github.aloxc.plugin.restplus.method.HttpMethod;
import com.github.aloxc.plugin.restplus.navigation.action.RestServiceItem;
import com.intellij.openapi.project.Project;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author leero
 */
public class RestServiceStructure {

    private JTree myTree;
    private final Project myProject;
    private int serviceCount = 0;

    private final RestServiceProjectsManager myProjectsManager;

    RestServiceDetail myRestServiceDetail;

//    private final Map<RestServiceProject, ModuleNode> myProjectToNodeMapping = new THashMap<>();

    public RestServiceStructure(Project project,
                                RestServiceProjectsManager projectsManager,
                                JTree tree) {
        myProject = project;
        myProjectsManager = projectsManager;
        myTree = tree;
        myTree.setCellRenderer(new ServiceTreeNodeCellRenderer());
        myRestServiceDetail = project.getComponent(RestServiceDetail.class);
        myRestServiceDetail.setProject(project);
        configureTree(tree);

        DefaultTreeModel model = (DefaultTreeModel) myTree.getModel();
        DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
        model.reload(root);
        myTree.expandPath(new TreePath(root));
        myTree.updateUI();
        UrlTreeSelectionListener urlTreeSelectionListener = new UrlTreeSelectionListener(project, myRestServiceDetail, myTree);
        myTree.addTreeSelectionListener(urlTreeSelectionListener);
        myTree.addMouseListener(urlTreeSelectionListener);
    }

    public Project getProject() {
        return myProject;
    }

    private void configureTree(JTree tree) {
        tree.setRootVisible(true);
        tree.setShowsRootHandles(true);
    }


    public RestServiceDetail getRestServiceDetail() {
        return myRestServiceDetail;
    }


    public void update() {
        List<RestServiceProject> projects = getRestServiceProjectList(myProject);
        Map<String, List<RestServiceProject>> serviceMap = new HashMap<>(16);
        serviceMap.put(Consts.SERVICES, projects);
        addSwaggerList(serviceMap);
        setSerivceNodes(serviceMap);
        resetRestServiceDetail();
    }

    private void addSwaggerList(Map<String, List<RestServiceProject>> serviceMap) {
        RestConfig restConfig = RestConfig.loadConfig(myProject);
        List<SwaggerConfig> swaggerConfigList = restConfig.getSwaggerConfigList();
        List<RestServiceProject> projects = new ArrayList<>();
        swaggerConfigList.stream().forEach(swaggerConfig -> {
            List<RestServiceItem> serviceItemList = new ArrayList<>();
            RestServiceProject restServiceProject = new RestServiceProject(swaggerConfig.getName(), serviceItemList);
            restServiceProject.setSwagger(true);
            projects.add(restServiceProject);
            swaggerConfig.getSwaggerRequestList().stream().forEach(swaggerRequest -> {
                RestServiceItem item = new RestServiceItem(null,null, swaggerRequest.getMethod().name(), swaggerRequest.getUrl(),
                        swaggerRequest.getMethod() == HttpMethod.POST_BODY,swaggerRequest.getName());
                item.setSwagger(true);
                item.setParentName(swaggerConfig.getName());
                serviceItemList.add(item);
            });
        });
        serviceMap.put(Consts.SWAGGERS,projects);
    }

    public static List<RestServiceProject> getRestServiceProjectList(Project project) {
        return RestServiceProjectsManager.getInstance(project).getServiceProjects();
    }

    /**
     * 分组
     *
     * @param projectList
     * @param group
     * @param sortx
     * @return
     */
    public static List<RestServiceProject> group(List<RestServiceProject> projectList,Group group,Sort sortx) {
        if(group == null) {
            group = Group.get();
        }
        if(sortx == null) {
            sortx = Sort.get();
        }
        final Sort sort = sortx;
        if (Group.MODULE.equals(group)) {
            projectList = projectList.stream().sorted(
                    (o1, o2) -> sort == Sort.ASC ? o1.getName().compareTo(o2.getName()) : o2.getName().compareTo(o1.getName()))
                    .collect(Collectors.toList());
        } else {
            List<RestServiceProject> newList = new ArrayList<>();
            List<RestServiceItem> serviceItemList = new ArrayList<>();
            projectList.stream().forEach(p -> {
                serviceItemList.addAll(p.getServiceItems());
            });
            Map<HttpMethod, List<RestServiceItem>> map = serviceItemList.stream().sorted().collect(Collectors.groupingBy(RestServiceItem::getMethod));
            map.keySet().stream().sorted(((o1, o2) -> sort == Sort.ASC ? o1.name().compareTo(o2.name()) : o2.name().compareTo(o1.name()))).
                    forEach(k -> newList.add(new RestServiceProject(k.name().toLowerCase(), map.get(k))));
            newList.iterator();
            projectList = newList;
        }
        Iterator<RestServiceProject> iterator = projectList.iterator();
        while (iterator.hasNext()) {
            RestServiceProject next = iterator.next();
            next.getServiceItems().sort((o1, o2) -> sort == Sort.ASC ? o1.getUrl().compareTo(o2.getUrl()) : o2.getUrl().compareTo(o1.getUrl()));
        }
        return projectList;
    }

    public void setSerivceNodes(Map<String, List<RestServiceProject>> projectMap) {
//        DefaultTreeModel model = (DefaultTreeModel) myTree.getModel();
//        ServiceRootNode root = new ServiceRootNode();
//        model.setRoot(root);
//        serviceCount = 0;
//        projects = group(projects);
//        for (RestServiceProject module : projects) {
//            ServiceModuleNode moduleNode = new ServiceModuleNode(module, module.getServiceItems().size());
//            root.add(moduleNode);
//            serviceCount += module.getServiceItems().size();
//            for (RestServiceItem service : module.getServiceItems()) {
//                ServiceNode serviceNode = new ServiceNode(service);
//                moduleNode.add(serviceNode);
//            }
//        }
//        root.setCount(serviceCount);
//        myTree.expandPath(new TreePath(root));
//
        DefaultTreeModel model = (DefaultTreeModel) myTree.getModel();
        DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
        root.removeAllChildren();
        model.setRoot(root);
        for(String pk : projectMap.keySet()){
            List<RestServiceProject> projects = projectMap.get(pk);
            ServiceRootNode rootNode = new ServiceRootNode();
            serviceCount = 0;
            if(Consts.SERVICES.equals(pk)){
                rootNode.setSwagger(false);
                projects = group(projects,null,null);
                for (RestServiceProject module : projects) {
                    ServiceModuleNode moduleNode = new ServiceModuleNode(module, module.getServiceItems().size());
                    rootNode.add(moduleNode);
                    serviceCount += module.getServiceItems().size();
                    for (RestServiceItem service : module.getServiceItems()) {
                        ServiceNode serviceNode = new ServiceNode(service,false);
                        moduleNode.add(serviceNode);
                    }
                }
                rootNode.setCount(serviceCount);
            }else{
                //处理swagge的
                rootNode.setSwagger(true);
                for (RestServiceProject swagger : projects) {
                    ServiceModuleNode swaggerNode = new ServiceModuleNode(swagger, swagger.getServiceItems().size());
                    swaggerNode.setSwagger(true);
                    rootNode.add(swaggerNode);
                    serviceCount += swagger.getServiceItems().size();
                    swagger.getServiceItems().sort(((o1, o2) ->
                        Sort.get() == Sort.DESC ?
                                o2.getUrl().compareTo(o1.getUrl()) :
                                o1.getUrl().compareTo(o2.getUrl())
                    ));
                    for (RestServiceItem service : swagger.getServiceItems()) {
                        ServiceNode serviceNode = new ServiceNode(service,true);
                        if(service.getMethod() == HttpMethod.POST_BODY){
                            service.setMethod(HttpMethod.POST);
                            service.setFoundRequestBody(true);
                        }
                        swaggerNode.add(serviceNode);
                    }
                }
                rootNode.setCount(serviceCount);
            }
            root.add(rootNode);
        }
        TreePath rootPath = new TreePath(root);
        myTree.expandPath(new TreePath(root));
        for (Enumeration<?> e = root.children(); e.hasMoreElements();) {
            TreeNode n = (TreeNode) e.nextElement();
            TreePath path = rootPath.pathByAddingChild(n);
            myTree.expandPath(path);
        }
        myTree.setRootVisible(false);
        myTree.updateUI();
    }


    /**
     * 重置相应标签
     * 重置方法
     * 重置url
     * 重置usercaseTree
     */
    private void resetRestServiceDetail() {
        myRestServiceDetail.resetRequestTabbedPane();
        myRestServiceDetail.setMethod(null);
        myRestServiceDetail.setUrl(null);
        myRestServiceDetail.setUserCase(null,null);
    }

}
